iT邦幫忙

2023 iThome 鐵人賽

DAY 16
1
自我挑戰組

Go in 3o系列 第 16

[Day16] Go in 30 - 錯誤處理 - recover

  • 分享至 

  • xImage
  •  

一、本篇提要

panic 狀況其實也非不能補救,Go語言提供了 recover() 函式,可以在某個 Goroutine 發生 panic 後取回控制權。

  • recover函式介紹
  • 處理 error 與 panic 的指導方針

二、recover

recover() 函式定義如下 :

func recover() interface{}

recover() 函數沒有參數,傳回值則是一個空值,這意味著回傳資料可以是任意型別。實際上 recover 傳回的值是你一開始傳給 panic 的值

**recover() 只是在使用 defer 延遲執行的函式中才有作用。**如前幾篇說到,延遲的函式會在外層函式結束前一刻執行。如果你在延遲執行函式中呼叫recover(),就可以恢復正常執行和停止panic。
相反的,但若recover函式是在延遲函式外的地方呼叫,就無法阻止panic

接下來看一個例子,包含 panic()、recover() 和 defer 時的過程 :

https://ithelp.ithome.com.tw/upload/images/20231001/20162693Re4kD8Byzs.png

  1. 先在main() 呼叫 a(),a() 呼叫 b()。
  2. 在 b()中發生panic。
  3. b() 中最後一個延遲執行函式,在 b() 結束前被呼叫,而這個函式執行了 recover()。
    4.最後的步驟,recover()阻止了panic,使流程正常回歸到a(),然後再回到b()。
package main

import (
	"errors"
	"fmt"
)

func main() {

    a()
    fmt.Println("這一行現在會印出")

}

func a() {
    b("good-bye")
    fmt.Println("返回上一層a()")
}

func b(msg string) {
    defer func() {
        if r:= recover(); r != nil {
            fmt.Println("b() 發生錯誤: ", r) //印出error
        }
    }()

    if msg == "good-bye" {
        panic(errors.New("阿北 出事了阿北")) // 引發panic
    }
}

執行結果 :
https://ithelp.ithome.com.tw/upload/images/20231001/20162693243NahSnY9.png

不管 b() 有沒有發生 panic 都會呼叫延遲執行的匿名函式,而這個匿名函式會去呼叫recover(),如果recover回傳回來的error值是nil,代表b()沒有發生panic,反之,就是有panic,則印出 error 值。

因為recover阻止b()內發生panic,所以panic就不會往上傳到a()或是main(),最終讓程式掛掉。

四、處理 error 與 panic 的方針

在錯誤處理這個主題中,知道了如何建立並回傳自訂error,必要時如何用panic()讓程式掛掉,最後則是如何從panic 復原、並在復原時根據傳panic()函式的error值來顯示錯誤訊息。

  1. 宣告自訂 error 值時,變數命名應以 Err 開頭,並遵照駝峰式命名。
  2. 如果函式或方法會回傳error,呼叫者就要在函式中事先判斷檢查內容是否為nil,如果未檢查可能會造成程式錯誤。
  3. 使用 panic() 時,請傳入一個error值為引數,不要只傳入空介面或空字串。
  4. 別拿error中的字串內容來做運算或比對
  5. 盡量少用panic(),只在真正不得已且需要保護程式的完整性時才使用它。

此外,在使用其他套件時,他們所引發的panic也不建議使用recover()去救,因為難確認救回來的套件式處於何種狀態,繼續使用上可能會有其他問題。

比起其他語言錯誤處理,Go語言處理語法相對單純,簡單的整理本主題:

  1. error是一個值,也就代表可以在函式中傳遞。
  2. 任何型別只要時做error介面型別,其變數值可以當成error
  3. panic和其他程式語言exception很像
  4. 發生panic可以用recover()放在defer延後執行函式中,使之將控制權還給程式
  5. error 命名應該要以Err開頭(以下例子)
package main

import (
"errors"
"fmt"
)

var (
ErrFileNotFound = errors.New("檔案找不到")
ErrNetworkConnection = errors.New("網路連線失敗")
)

func main() {
err := openFile("example.txt")
if err != nil {
fmt.Println("發生錯誤:", err)
}

err = establishNetworkConnection()
if err != nil {
fmt.Println("發生錯誤:", err)
}
}

func openFile(filename string) error {
// 模擬檔案未找到的狀況
return ErrFileNotFound
}

func establishNetworkConnection() error {
// 模擬網路連線失敗的情況
return ErrNetworkConnection
}

以上就是Go錯誤處理的主題整理,下一篇開始會進入到介面的部分。


上一篇
[Day15] Go in 30 - 錯誤處理 panic
下一篇
[Day17] Go in 30 - 介面(interface)
系列文
Go in 3o30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言